/*Author: Andy Wu, Matthew Lorenzo Assignment: Lab1, Calculator*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

/*problems with registry operations using 3 arguments(has been fixed as of 8PM of 01/19/2011);
 still working on remembering registry history part -AW*/
/*registry history part added by Matt, error in storing reg hist in readreg function, i.e. keeps reinitializing registers to 0*/
/*registry history seemingly fixed in collaboration(5PM 01262012)*/

/*Functions for operations*/

double binplus(double x,double y)
	{
	return x+y;
	}

double binmin(double x, double y)
	{
	return x-y;
	}
double unplus(double x)
	{
	if(x<0)
		{
		return -x;
		}
	else
		{
		return x;
		}
	}
double unmin(double x)
	{
	return -x;
	}
double mult(double x,double y)
	{
	return x*y;
	}
double Div(double x, double y)
	{
	return x/y;
	}

int isNumeric (const char * s)
	{
	if (s == NULL || *s == '\0' || isspace(*s))
		{return 0;}
	char * p;
	strtod (s, &p);
	return *p == '\0';
	}

/****Setting up access to registers*****/
/*This function makes sure the character used in the register is a valid reprensation of the register, returns 1 if it is, returns 0 if not*/
int isregister(char c)
	{
	int regnum;
	regnum=(int)c;
	if ((regnum>64)&&(regnum <123))
    		{
		return 1;
		}
	else
		{
		return 0;
		}
	}

/*This function takes a character for its ASCII value and then returns a value from 0 to 25, to be used to set the location of the register in the array */
int registerpos(char c)
	{
	int regnum; /*declare var of type int to temporarily assign one ascii value to represent the register*/ 
	regnum = (int)c;
	if ((regnum>64) && (regnum<91))
		{
		regnum=regnum-65; /* converting the ascii value to lowercase */
		return regnum;
		}
	else     /*If register is capitalized*/
		{
		regnum=regnum-97;
		return regnum;
		}
	}


int mHist()
	{
	FILE *Hist;/*Reghist*/
	Hist=fopen("history.txt","r");	/*attempts to open history.txt*/
	if(Hist==0)	/*tests if history.txt file exists and prints error message if it isn't*/
		{
		printf("No existing history of commands.\n");
		return 1;
		}
	else if((fgetc(Hist))==EOF)	/*condition for if nothing is in the file*/
		{
		return 0;
		}
	else
		{
		if(read_history("history.txt"))	/*check if txt file is read successfully*/
			{
			printf("Error while reading history file.\n");
			return 2;
			}
		return 0;
		}
	}

int main(void)
	{
	double reg[26]={0};	/* array used to store the values of the register*/
	double ACC=0,val1=0,val2=0,tempv2=0,oACC=0;		/*declare and initializes variable to accumulate values*/
	int arg=0,a;	/*declare and initializes variables to store first numerical input, store second
					numerical input, and number of arguments given by user, respectively*/
	char *cp;		/*string to store user inputs to be stored in history.txt*/
	char a1[2],a2[2],a3[2],a4[2],*dum,*dum2;		/*char variable to store operators*/
	int regpos;		/*the position in register array as an int*/
	mHist();		/*call on function to take care of stored history*/
	
	/*saving register values initialization part*/
	FILE *fp;
	fp=fopen("registers.txt","r");
	if(fp)		/*if file exists, read value from file to registers*/
		{
		fread(reg,8,26,fp);
		fclose(fp);
		}

	while(1)		/*infinite loop for calculator to run*/
		{
/*		for(a=0;a<26;a++){printf("reg[%d]=%lf\n",a,reg[a]);}*/	/*prints out register values for checking*/
		if(ACC>999999999)	/*checking if ACC exceeded preset calculator digit limit of 9*/
			{
			printf("Sorry, value exceeded calculator limit of 9 digits.\n");
			if((arg!=2))
				{
				ACC=oACC;
				}
			else
				{
				ACC=ACC/tempv2;
				}
			}
		if(ACC==(int)ACC)	/*check if value stored in ACC is of type int*/
			{
			printf("%d",(int)ACC);	/*print stored value from accumulator variable as an int*/
			}
		else
			{
			printf("%6.6f",ACC);
			}

		cp=readline(":");	/*prints the ":" and copy the user input as a string into a variable*/
		if (cp!=NULL)	/*if there is an input, store it into history and write it onto history.txt*/
			{
			add_history(cp);
			write_history("history.txt");
			}
        
		else	/*when readline gets a NULL (i.e. when ctrl-D is entered), it breaks*/
			{
			break;
			}

		arg=sscanf(cp,"%s %s %s %s",a1,a2,a3,a4); /*tries to check for arguments in form: 'x = 5'*/

/*scanf(cp,"%lf %s %lf",&val1,a2,&val2);
*/a=strlen(a1); /*printf("line179 Strlen(a1)=%d\n",a); a=strlen(a3); printf("Strlen(a3)=%d\n",a);
printf("line180 ACC=%lf,arg=%d,a1=%s,a3=%s,a2(op)=%s,val1=%lf,val2=%lf,a4=%s\n",ACC,arg,a1,a3,a2,val1,val2,a4);
*/

			if (arg==EOF)		/*in the case of error in reading argument, ends program*/
				{
				free(cp);
				break;
				}
			else if(arg>3)
				{
				printf("Error, please enter at most 3 arguments.\n");
				a1[0]=0;a2[0]=0;a3[0]=0;free(cp);continue;
				}

			else if(arg==1)	/*if statement to check if only one character variable is entered*/
				{
				sscanf(cp,"%lf",&val1);
				dum=a1;
				if((strlen(a1)==1)&&(isregister(a1[0])))
					{
					regpos=registerpos(a1[0]);
					oACC=ACC;	/*storing old value of ACC before it changes just in case we need it later*/
					ACC=reg[regpos];
					a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
					continue;
					}
				else if((strlen(a1)==1)&&(strcmp(a1,"-")==0))	/*when only '-' is entered, value is ACC becomes negative of original ACC value*/
					{
					oACC=ACC;
					ACC=unmin(ACC);
					a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
					continue;
					}
				else if((strlen(a1)==1)&&(strcmp(a1,"+")==0))	/*when only '+' is entered, value is ACC becomes positive of original ACC value*/
					{
					oACC=ACC;
					ACC=unplus(ACC);
					a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
					continue;
					}
				else
					{
					if(isNumeric(a1))	/*a1 is a numerical value*/
						{
						if((strlen(a1)>9))
							{
							printf("Please only input values with 9 digits or less.\n");
							val1=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
							continue;
							}
						else
							{
							oACC=ACC;
							ACC=val1;
							val1=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
							continue;
							}
						}	
					else
						{
						printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
						val1=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}
					}
				}
			else if(arg==2)	/*when 2 arguments are entered*/
				{
				sscanf(cp,"%lf %s",&val1,a2);	/*check case where arg detects only 2 inputs: an operator and a number*/
				sscanf(cp,"%s %lf",a1,&val2);	/*check case where arg detects only 2 inputs: an operator and a number*/
				dum=a1;dum2=a2;

				if((strlen(a1)!=1))	/*erroneous input for when 2 arguments are entered*/
					{
					printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
					tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
					continue;
					}
				if(isNumeric(a2))	/*check if a number is entered*/
					{
					if((strlen(a1)>9))	/*if it's not a valid number (9 digits or less)*/
						{
						printf("Please only input values with 9 digits or less.\n");
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}
					else if((strlen(a2)<=9)&&(strlen(a1)==1))	/*valid number and semi-valid operator(only checking for length of op in this step)*/
						{
						if(strcmp(a1,"+")==0)	/*check for '+' operator, in 2 arg binary operation with numbers*/
							{
							oACC=ACC;
							ACC=binplus(ACC,val2);
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
							continue;
		 					}
						else if(strcmp(a1,"-")==0)	/*check for '+' operator, in 2 arg binary operation with numbers*/
							{
							oACC=ACC;
							ACC=binmin(ACC,val2);
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
							continue;
							}
						else if(strcmp(a1,"*")==0)	/*check for '+' operator, in 2 arg binary operation with numbers*/
							{
							oACC=ACC;
							ACC=mult(ACC,val2);
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
							continue;
							}
							
						else if(strcmp(a1,"/")==0)	/*check for '+' operator, in 2 arg binary operation with numbers*/
							{							
							if(val2==0)
								{
								printf("Error: cannot divide by zero. Please try again.\n");
								tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
								continue;
								}
							else 
								{
								oACC=ACC;
								ACC=Div(ACC,val2);
								tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
								continue;
								}
							}
						else	/*invalid operator, in 2 arg binary operation with numbers*/
							{
							printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",\"@\",or \"=\".\n");
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
							continue;
							}
						}
					}

				else if((strlen(a2)==1)&&(isregister(a2[0])))	/*handling operations with register when 2 arguments are entered*/
					{
					if(strcmp(a1,"@")==0) /*check for '@' operator, for storing ACC in register*/ 
						{
						regpos=registerpos(a2[0]);
						reg[regpos]=ACC;
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						/*part to write register values into registers.txt file*/
						fp=fopen("registers.txt","w");fwrite(reg,8,26,fp);fclose(fp);
						continue;
						}
					else if(strcmp(a1,"+")==0) /*check for '+' operator, in 2 arg binary operation with register*/ 
						{
						regpos=registerpos(a2[0]);
						oACC=ACC;
						ACC=binplus(ACC,reg[regpos]);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}
					else if(strcmp(a1,"-")==0) /*check for '-' operator*/ 
						{
						regpos=registerpos(a2[0]);
						oACC=ACC;
						ACC=binmin(ACC,reg[regpos]);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}
					else if(strcmp(a1,"*")==0) /*check for '*' operator*/ 
						{
						regpos=registerpos(a2[0]);
						oACC=ACC;
						ACC=mult(ACC,reg[regpos]);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}
					else if(strcmp(a1,"/")==0) /*check for '/' operator*/ 
						{
						regpos=registerpos(a2[0]);
						oACC=ACC;
						ACC=Div(ACC,reg[regpos]);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}
					}
				else if(!((strlen(a2)==1)&&(isregister(a2[0]))))	/*register is more that one character long*/
					{
					printf("Invalid Registry value! Specify a single character registry between a-z and keep a space between @ and the register.\n");
					tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
					continue;
					}
				else	/*print error message when all else fails for 2 arguments*/
					{
					printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
					tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
					continue;
					}
				}

			else if(arg==3)	/*when 3 arguments are entered*/
				{
				if(isNumeric(a3)){val2=atof(a3);}	/*get value of val2 if is number*/
				if(isNumeric(a1)){val1=atof(a1);}	/*get value of val1 if is number*/	
				dum=a1;dum2=a3;

				/*part to handle 3 argument register inputs with number operations (i.e. "x * 5")*/
				if (((strlen(a1)==1)&&(isregister(a1[0])))&&(isNumeric(a3)))
					{
					regpos=registerpos(a1[0]);
					val1=reg[regpos];

					if(strcmp(a2,"=")==0)	/*case where "y = 5" is entered*/
						{

						if((strlen(a1)==1)&&(isregister(a1[0])))
							{
							regpos=registerpos(a1[0]);
							reg[regpos]=atof(a3);

							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						/*part to write register values into registers.txt file*/
						fp=fopen("registers.txt","w");fwrite(reg,8,26,fp);fclose(fp);
							continue;
							}
						else
							{
							printf("Error: To set a value to a registry, use the format 'register = number'\n");
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
		/*part to write register values into registers.txt file*/
		fp=fopen("registers.txt","w");fwrite(reg,8,26,fp);fclose(fp);
							continue;
							}
						}

					else if((strcmp(a2,"+"))==0)
						{
						oACC=ACC;
						ACC=binplus(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
		/*part to write register values into registers.txt file*/
		fp=fopen("registers.txt","w");fwrite(reg,8,26,fp);fclose(fp);
						continue;
						}
					else if((strcmp(a2,"-"))==0)
						{
						oACC=ACC;
						ACC=binmin(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
		/*part to write register values into registers.txt file*/
		fp=fopen("registers.txt","w");fwrite(reg,8,26,fp);fclose(fp);
						continue;
						}
					else if(strcmp(a2,"*")==0)
						{
						oACC=ACC;
						ACC=mult(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if(strcmp(a2,"/")==0)
						{
						if(val2==0)
							{
							printf("Error: cannot divide by zero. Please try again.\n");
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						else 
							{
							oACC=ACC;
							ACC=Div(val1,val2);
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						}
					else
						{
						printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
					continue; /*return to beginning of loop, after storing arg, to have value of arg from possible later use*/
					}
			
				/*part to handle 3 argument register inputs with number operations with number in front (i.e. "6 + z")*/
				else if((isNumeric(a1))&&(strlen(a3)==1)&&(isregister(a3[0]))&&(strlen(a2)==1))
					{
					regpos=registerpos(a3[0]);
					val2=reg[regpos];
					if((strcmp(a2,"+"))==0)
						{
						oACC=ACC;
						ACC=binplus(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if((strcmp(a2,"-"))==0)
						{
						oACC=ACC;
						ACC=binmin(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if(strcmp(a2,"*")==0)
						{
						oACC=ACC;
						ACC=mult(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if(strcmp(a2,"/")==0)
						{
						if(val2==0)
							{
							printf("Error: cannot divide by zero. Please try again.\n");
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						else 
							{
							oACC=ACC;
							ACC=Div(val1,val2);
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						}
					else
						{
						printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					}
				
				/*part to handle 3 argument register inputs*/
				else if ((strlen(a1)==1)&&(isregister(a1[0]))&&(strlen(a3)==1)&&(isregister(a3[0]))&&(strlen(a2)==1))
					{
					regpos=registerpos(a1[0]);
					val1=reg[regpos];
					regpos=registerpos(a3[0]);
					val2=reg[regpos];

					if((strcmp(a2,"+"))==0)
						{
						oACC=ACC;
						ACC=binplus(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;					
						}
					else if((strcmp(a2,"-"))==0)
						{
						oACC=ACC;
						ACC=binmin(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if(strcmp(a2,"*")==0)
						{
						oACC=ACC;
						ACC=mult(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if(strcmp(a2,"/")==0)
						{
						if(val2==0)
							{
							printf("Error: cannot divide by zero. Please try again.\n");
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						else 
							{
							oACC=ACC;
							ACC=Div(val1,val2);
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						}
					else
						{
						printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					}

				/*3 argument binary operation with two number arguments (i.e. "3 * 4")*/
				else if((strlen(a2)==1)&&(isNumeric(a1))&&(isNumeric(a3)))
					{
					if((strcmp(a2,"+"))==0)
						{
						oACC=ACC;
						ACC=binplus(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if((strcmp(a2,"-"))==0)
						{
						oACC=ACC;
						ACC=binmin(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if(strcmp(a2,"*")==0)
						{
						oACC=ACC;
						ACC=mult(val1,val2);
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
						continue;
						}
					else if(strcmp(a2,"/")==0)
						{
						if(val2==0)
							{
							printf("Error: cannot divide by zero. Please try again.\n");
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						else
							{
							oACC=ACC;
							ACC=Div(val1,val2);
							tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp); 
							continue;
							}
						}
					else if(strcmp(a2,"=")==0)
						{
						printf("Invalid input! The 3 argument binary operation with 2 numbers doesn't support the = sign operator.\n");
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}
					else
						{
						printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
						tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
						continue;
						}	
					}
				
				else
					{
					printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
					tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
					continue;
					}
				}
			else
				{
				printf("Invalid input! Remember to enter spaces between binary operators and numbers, and be sure to write both numbers if performing a binary operation. Please only input these operators: \"+\",\"-\",\"*\",\"/\",or \"=\".\n");
				tempv2=val2;val1=0;val2=0;a1[0]=0;a2[0]=0;a3[0]=0;free(cp);
				continue;
				}

		}
	return 0;
	}
